﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
using Microsoft.WindowsAzure.ServiceRuntime;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Text.RegularExpressions;

namespace DDFileSyncPlugin
{
    public class CloudDriveManagerRole
    {
        const string DRIVE_LOCAL_STORAGE_NAME = "dddrivelocalcache";
        const string DCACHE_LOCATION = "dddrivecache";
        const int DEFAULT_CACHE_SIZE_IN_MB = 100;
        const string STORAGE_ACCOUNT_SETTING = "com.dynamicdeploy.ddfilesync.DrivesStorageAccountConnectionString";
        const string PERSISTENT_DRIVE_SETTINGS = "com.dynamicdeploy.ddfilesync.PersistentDrives";
        const string REGEX_ROUNDBRACKETS = @"\((.*?)\)";
       // public static CloudDriveManager _cloudDriveManager = null;
        public static readonly IDictionary<string, PersistentDriveConfig> PersistentDriveConfigs = new Dictionary<string, PersistentDriveConfig>();
        public static readonly IList< CloudDriveManager> CloudDriveManagers = new List<CloudDriveManager>();
        const int DRIVE_MOUNT_RETRY = 3;
        public static bool ENABLE_PERSISTENT_DRIVES = false;
       
        private static bool FillConfigurationObject(string configNVs, ref PersistentDriveConfig configObj)
        {
            bool ret = true;
            if (!string.IsNullOrEmpty(configNVs) && configObj != null)
            {
                string[] configPropArr = configNVs.Split('=');
                switch (configPropArr[0])
                {
                    //case PersistentDriveConfig.CACHE_NAME:
                    //    if (!string.IsNullOrEmpty(configPropArr[1]))
                    //    {
                    //        configObj.CacheName = SyncService.ExpandKeywords(configPropArr[1].Trim()); 
                           
                    //        Trace.TraceInformation("CacheName="
                    //            + configObj.CacheName.ToString());
                    //    }
                    //    else
                    //    {
                          
                    //        Trace.TraceInformation("Cache name not found. Using default " + configObj.CacheName);
                    //    }
                    //    break;
                    case PersistentDriveConfig.CACHE_SIZE_IN_MB:


                        if (!string.IsNullOrEmpty(configPropArr[1]))
                        {
                            configObj.CacheSizeInMB = int.Parse(configPropArr[1].Trim());
                            Trace.TraceInformation("Cache Size in MB ="
                                + configObj.CacheSizeInMB.ToString());
                        }
                        else
                        {
                           
                            Trace.TraceInformation("Using default cache size " + configObj.CacheSizeInMB);
                        }
                        break;
                    case PersistentDriveConfig.DRIVE_ADDRESS:

                        if (!string.IsNullOrEmpty(configPropArr[1]))
                        {
                            configObj.VHDAddress = SyncService.ExpandKeywords( configPropArr[1].Trim());
                            Trace.TraceInformation("VHDAddress="
                                + configObj.VHDAddress.ToString());
                            
                        }
                        else
                        {

                            Trace.TraceInformation("VHD Address not present. Drives will fail");
                            return false;
                        }

                        break;
                    case PersistentDriveConfig.DRIVE_LETTER:

                        if (!string.IsNullOrEmpty(configPropArr[1]))
                        {
                            configObj.DriveLetter = configPropArr[1].Trim();
                            Trace.TraceInformation("Drive Letter="
                                + configObj.DriveLetter.ToString());
                            configObj.CacheName = configObj.DriveLetter;
                        }
                        else
                        {

                            Trace.TraceInformation("Drive Letter not present. Drives will fail");
                            return false;
                        }

                        break;
               
                    default:

                        break;


                };


            }

            return ret;
        }
        public static bool LoadPersistentDriveConfigurations()
        {
            bool allIsWell = true;


            try
            {
                string configStrs = SyncService.GetConfigValueAsString(PERSISTENT_DRIVE_SETTINGS);
                string storageAccountConnectionString = SyncService.GetConfigValueAsString(STORAGE_ACCOUNT_SETTING);

                if (!string.IsNullOrEmpty(configStrs))
                {




                    Regex re = new Regex(REGEX_ROUNDBRACKETS);

                    foreach (Match m in re.Matches(configStrs))
                    {
                        string eachConfigObject = m.Value.Substring(1, m.Value.Length - 2);

                        PersistentDriveConfig configObj = new PersistentDriveConfig()
                        {
                            CacheLocalStorageName = DRIVE_LOCAL_STORAGE_NAME,
                            DriveConnectionString = storageAccountConnectionString,
                            CacheName = DCACHE_LOCATION,
                             CacheSizeInMB = DEFAULT_CACHE_SIZE_IN_MB
                             
                              

                        };

                        string[] configProsArr = eachConfigObject.Split(';', ',');

                        foreach (string configNV in configProsArr)
                        {
                            try
                            {


                                if (!FillConfigurationObject(configNV, ref configObj))
                                {

                                    return false;
                                }
                              



                            }
                            catch (Exception ex)
                            {
                                Trace.TraceError("Error loading configuration object from configuration string " + configStrs + Environment.NewLine + ex.Message);
                                allIsWell = false;
                            }
                        }//foreach nv

                        if (allIsWell)
                        {
                            if (!PersistentDriveConfigs.ContainsKey(configObj.DriveLetter))
                            {
                                PersistentDriveConfigs.Add(configObj.DriveLetter, configObj);
                            }

                        }
                        else
                        {
                           
                            return false;
                        }
                       
                    }//for regex match

                }
                else
                {

                    Trace.TraceError("LoadPersistentDriveConfigurations() backup restore configurations were empty.");
                    allIsWell = false;
                }

            }
            catch (Exception ex)
            {

                Trace.TraceError("LoadPersistentDriveConfigurations() Error loading configuration for drives " + ex.Message);
                allIsWell = false;
            }

         

            return allIsWell;


        }
        public void Run()
        {
            var storageAccount = CloudStorageAccount.FromConfigurationSetting(STORAGE_ACCOUNT_SETTING);

          
            while (true)
            {
                try
                {
                    Thread.Sleep(TimeSpan.FromSeconds(Program.TIMEINSECSFORINFINITEINITWAIT * 10));
                    CheckDrives();
                   // Trace.TraceInformation("Working - from Cloud Drive Manager", "Information");
                }
                catch (Exception ex)
                {
                    Trace.TraceError("CloudDriveManagerRole fail in Run "+ ex.Message);
                }
            }

        }

        private void CheckDrives()
        {
           
            foreach(CloudDriveManager cdm in CloudDriveManagers)
            {
                try
                {

                    if(!cdm.VerifyDriveLetter())
                    {

                        Trace.TraceError(string.Format("Cloud Drive verification failed for drive {0} with drive letter {1}. ", cdm.VhdName, cdm.DriveLetter));
                    }

                }
                catch (Exception ex)
                {

                    Trace.TraceError(string.Format("Error checking cloud drive {0} with drive letter {1}. Exception {2}.", cdm.VhdName, cdm.DriveLetter, ex.Message));
                }


            }
        }


        public bool OnStart()
        {
            try
            {
                Initialize();
                return MountAllDrives();
            }
            catch (Exception ex)
            {
                Trace.TraceError("fail on onstart "+ ex);
                return false;
            }
            
        }

       

        static bool MountAllDrives()
        {
            Trace.TraceInformation("Mounting all Drives.");
            int driveCount = PersistentDriveConfigs.Count;

            foreach (KeyValuePair<string, PersistentDriveConfig> kv in PersistentDriveConfigs)
            {
                //Mount each drive
                Trace.TraceInformation(string.Format("Mounting drive {0} with letter {1}", kv.Value.VHDAddress, kv.Key));
                bool mounted = MountDrive(kv.Value, driveCount, DRIVE_MOUNT_RETRY);
                if (mounted)
                {
                    Trace.TraceInformation(string.Format("Mounted drive {0} with letter {1}", kv.Value.VHDAddress, kv.Key));
                }
                else
                {
                    Trace.TraceError(string.Format("Failed to mount drive {0} with letter {1}", kv.Value.VHDAddress, kv.Key));
                    return false;
                }

            }

            Trace.TraceInformation("MountAllDrives()");
            return true;
        }

      
        static bool MountDrive(PersistentDriveConfig configObj, int driveCount, int retries)
        {
            if (retries == 0)
            {
                Trace.TraceError(string.Format("Exceeded retries for mounting drive {0} with address {1}", configObj.DriveLetter, configObj.VHDAddress));
                return false;
            }
            try
            {
               
                CloudStorageAccount account = CloudStorageAccount.Parse(configObj.DriveConnectionString);
                Trace.TraceInformation("Loaded storage account " + account.Credentials.AccountName);
               // string dCacheName = RoleEnvironment.GetConfigurationSettingValue(configObj.CacheLocalStorageName);
               // Trace.TraceInformation("Loaded cache " + dCacheName);
                LocalResource cache = RoleEnvironment.GetLocalResource(DRIVE_LOCAL_STORAGE_NAME);
                Trace.TraceInformation(string.Format("Loaded local storage {0} with max size {1} MB.",  cache.Name, cache.MaximumSizeInMegabytes));
                int cacheSize = cache.MaximumSizeInMegabytes / driveCount;
                CloudDriveManager cloudDriveManager = new CloudDriveManager(account, configObj.VHDAddress, char.Parse(configObj.DriveLetter), cache, cacheSize, configObj.CacheName);
                cloudDriveManager.CreateDrive();
                bool mounted = cloudDriveManager.Mount();
                if (mounted)
                {
                    CloudDriveManagers.Add(cloudDriveManager);
                    return true;
                }
                else
                {
                    Trace.TraceError(string.Format("Mounting of drive {0} was not successful as drive letter {1}. Retries remining {2}", configObj.VHDAddress, configObj.DriveLetter, retries));
                    Thread.Sleep(TimeSpan.FromSeconds(10));
                    MountDrive(configObj, driveCount, --retries);
                }
               // return true;
            }
            catch (Exception ex)
            {
                Trace.TraceError("fail on mountalldrives. Retries remaining " + retries + " " + ex.Message);
                Thread.Sleep(TimeSpan.FromSeconds(10));
                MountDrive(configObj, driveCount, --retries);
            }

            return false;
        }
        /// <summary>
        /// Cloud Initialization area
        /// </summary>
        void Initialize()
        {
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
            });

            ENABLE_PERSISTENT_DRIVES = bool.Parse(SyncService.GetConfigValueAsString("com.dynamicdeploy.ddfilesync.EnablePersistentDrives"));

            if (ENABLE_PERSISTENT_DRIVES)
            {
               // string dCacheName = RoleEnvironment.GetConfigurationSettingValue(DRIVE_LOCAL_STORAGE_NAME);
                LocalResource cache = RoleEnvironment.GetLocalResource(DRIVE_LOCAL_STORAGE_NAME);
                const int TRIES = 30;

                // Temporary workaround for ERROR_UNSUPPORTED_OS seen with Windows Azure Drives
                // See http://blogs.msdn.com/b/windowsazurestorage/archive/2010/12/17/error-unsupported-os-seen-with-windows-azure-drives.aspx
                for (int i = 0; i < TRIES; i++)
                {
                    try
                    {
                        CloudDrive.InitializeCache(cache.RootPath, cache.MaximumSizeInMegabytes);
                        break;
                    }
                    catch (CloudDriveException ex)
                    {
                        if (!ex.Message.Equals("ERROR_UNSUPPORTED_OS"))
                        {
                            throw;
                        }

                        if (i >= (TRIES - 1))
                        {
                            // If the workaround fails then it would be dangerous to continue silently, so exit 
                            Trace.TraceInformation("Workaround for ERROR_UNSUPPORTED_OS see http://bit.ly/fw7qzo FAILED", "Error");
                            System.Environment.Exit(-1);
                        }

                        Trace.TraceInformation("Using temporary workaround for ERROR_UNSUPPORTED_OS see http://bit.ly/fw7qzo", "Information");
                        Thread.Sleep(10000);
                    }
                }
                CloudDrive.InitializeCache(cache.RootPath + DCACHE_LOCATION, cache.MaximumSizeInMegabytes);

                var storageAccount = CloudStorageAccount.FromConfigurationSetting(STORAGE_ACCOUNT_SETTING);

                LoadPersistentDriveConfigurations();

            }
            else
            {
                Trace.TraceInformation("Persistent Drives Disabled.");

            }

        }

    }
}
